﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace GameFramework.Geometry
{
    public class Ellipse
    {
        /// <summary>
        /// Creates a new instance of Ellipse.
        /// </summary>
        /// <param name="center">Position of center.</param>
        /// <param name="r1">Horizontal radius.</param>
        /// <param name="r2">Vertical radius.</param>
        /// <param name="samples">Number of samples to calculate vertices.</param>
        public Ellipse(Vector2 center, float r1, float r2, int samples)
        {
            this.center = center;
            this.r1 = r1;
            this.r2 = r2;
            this.samples = samples;

            for (int i = 0; i <= samples; ++i)
            {
                float currentX = r1 * (-1f + 2f * i / samples);
                float currentY = (r2 / r1) * (float) Math.Sqrt(r1 * r1 - currentX * currentX);
                ellipseVertices.Add(new Vector2(currentX, currentY));
            }

            for (int i = samples - 1; i > 0; --i)
            {
                float currentX = r1 * (-1f + 2f * i / samples);
                float currentY = (r2 / r1) * (float) Math.Sqrt(r1 * r1 - currentX * currentX);
                ellipseVertices.Add(new Vector2(currentX, -currentY));
            }
        }

        /// <summary>
        /// Calculates radius at certain point.
        /// </summary>
        /// <param name="position">Point over ellipse.</param>
        /// <exception cref="PointNotOnEllipseException"></exception>
        /// <returns>Radius.</returns>
        public float GetRadiusEllipse(Vector2 position)
        {
            float x = position.X - center.X,
                y = position.Y - center.Y;

            if ((x * x) / (r1 * r1) + (y * y) / (r2 * r2) != 1)
                throw new PointNotOnEllipseException(this, position);

            return position.Length();
        }

        /// <summary>
        /// Creates an extended ellipse by an amount.
        /// </summary>
        /// <param name="extentionAmount">Amount of extention.</param>
        /// <returns>Extended ellipse.</returns>
        public Ellipse GetExtendedEllipse(float extentionAmount)
        {
            return new Ellipse(center, r1 + extentionAmount, r2 + extentionAmount, samples);
        }

        /// <summary>
        /// Circumference of the ellipse.
        /// </summary>
        public float Circumference
        {
            get
            {
                return 2 * MathHelper.Pi * (float) (Math.Sqrt((r1 * r1 + r2 * r2) / 2.0f));
            }
        }

        /// <summary>
        /// Vertices of ellipse calculated by r1, r2 and sample count.
        /// </summary>
        public List<Vector2> EllipseVertices
        {
            get { return ellipseVertices; }
            set { ellipseVertices = value; }
        }

        /// <summary>
        /// Position of the center.
        /// </summary>
        public Vector2 Center
        {
            get { return center; }
            set { center = value; }
        }

        /// <summary>
        /// Vertical radius.
        /// </summary>
        public float R2
        {
            get { return r2; }
            set { r2 = value; }
        }

        /// <summary>
        /// Horizontal radius.
        /// </summary>
        public float R1
        {
            get { return r1; }
            set { r1 = value; }
        }

        /// <summary>
        /// If a point is not on an ellipse as expected this Exception class will be thrown.
        /// </summary>
        public class PointNotOnEllipseException : Exception
        {
            public PointNotOnEllipseException(Ellipse ellipse, Vector2 point)
                : base("Point (" + point.X + ", " + point.Y + ") is not on the ellipse r1 = " +
                    ellipse.R1 + " r2 = " + ellipse.R2 + " and Center (" + ellipse.Center.X + ", " + ellipse.Center.Y + ").")
            {
            }
        }

        int samples;
        float r1, r2;
        Vector2 center;
        List<Vector2> ellipseVertices = new List<Vector2>();
    }
}
